is the ideal node for tight control of objects interacting with the game world.
Characters in 3D platformers and FPS games, moving platforms, and anything else you want to control that also needs to collide with other game entities are great candidates for .
If you want more realistic Newtonian physics and to let the physics engine calculate movement for you, you’ll want to use instead.
In this guide, you will learn:
We’ll wrap up with your questions, troubleshooting help, and tips.
Contents:
Like other physics bodies in Godot, a requires one or more or nodes as children.
The collision nodes define the shape the physics engine uses to check for collisions with the environment.
You can always use more than one shape to approximate the shape of your character or entity.
comes packed with functions to create character controllers with ease:
move_and_slide() and
move_and_slide_with_snap() handle a lot under the hood for
you and help you start coding movement quickly.is_on_floor(),
is_on_wall(), and is_on_ceiling() tell you if
your entity is on the floor, against a wall, or on the ceiling. These
are great for 3D platformers.move_and_collide() and test_motion()
functions.The most valuable function of a is move_and_slide().
It takes a velocity and moves your entity accordingly in
one frame.
move_and_slide() applies delta time delta internally,
meaning bodies move at the same speed regardless of the framerate the
user is experiencing.
If the body hits an obstacle, the function slides the body against it.
Technically, it takes the leftover motion at the collision point and projects the remaining velocity alongside it.
If any motion remains after colliding, the node repeats the operation
up to 5 times, allowing your entity to slide smoothly along
curved obstacles. You can adjust this with the max_slides
parameter of move_and_slide().
move_and_slide() also takes other moving bodies into
account. For example, if a character is on a moving platform, both move
together.
The move_and_slide_with_snap() function is a more
powerful alternative for worlds with slopes. When walking on a slope,
you can use it to snap your character to the slope.
You do that using the snap argument, a vector representing a distance to the floor. If your character is too close to the floor, it will snap to it.
Both the move_and_slide() and
move_and_slide_with_snap() functions also allow you to
define the “up” vector.
This argument helps the engine determine what direction is “up” and, therefore, where the floor, ceilings, and walls are.
Note: We put “up” in quotes because you can use that to reverse gravity in your game, allow characters to move around planets, and more.
You’ll want to change the up vector in a game where you change the
direction of gravity, turning the walls and ceiling into the floor. For
games that have normal gravity Vector3.UP is the default
unit vector pointing upwards on the y-axis.
Game: Super Mario Galaxy
The default value for up is Vector3.ZERO. In this case,
the treats all surfaces as a wall.
What if you want a projectile to bounce against surfaces? With
move_and_slide(), the projectile slides alongside walls
after a collision.
Or perhaps you want a feel you cannot achieve easily with
move_and_slide(), like a specific style of character
movement.
That’s what move_and_collide() is for. This function
moves the body with a motion vector. This is different
from move_and_slide(), which instead takes a
velocity.
The move_and_collide() function moves the character
given that input motion and collides along the way. If a collision
occurs, the function returns a KinematicCollision object,
providing information about what happened. You can then use that
information to code your intended movement.
Godot uses move_and_collide() as a base internally for
move_and_slide() and
move_and_slide_with_snap().
When collisions occur, you can get detailed information about them.
Whether you use move_and_slide() or
move_and_collide(), you can get a
KinematicCollision instance to know where, when, and
between which physics shapes the collision occurred.
If you use move_and_collide() and a collision occurs,
the function returns the KinematicCollision instance. We
typically store and use it like so:
var collision := move_and_collide(motion)
if collision:
# ...With move_and_slide(), as we saw above, the engine may
move your body multiple times to smooth out its motion. The function
itself returns a new velocity, taking into account any obstacle that
affects your body.
If you want collision information, you need to request it. To do so,
you’ll use two functions: get_slide_count() and
get_slide_collision().
The get_slide_count() function tells you the number of
collisions that occurred after calling move_and_slide().
You use it to ensure a given collision exists when calling
get_slide_collision(), like so:
move_and_slide(velocity)
for index in get_slide_count():
var collision := get_slide_collision(index)
# ...provides three functions to know if your entity is touching a surface at any point in time:
is_on_floor().is_on_wall().is_on_ceiling().They return true if the body is against the
corresponding surface.
These functions only work after moving your and having it collide with that surface. Otherwise, the physics engine assumes your entity is in the air.
If you are using move_and_slide(), you’ll want to apply
gravity to your characters even when grounded to ensure they collide
with the floor. If you are using
move_and_slide_with_snap(), this is not needed as the
snapping ensures the ground collision.
You can use those functions to check if the player can jump without a
dedicated can_jump variable.
Many of these demos build upon the same base code, unlike other node essentials guides.
We created a base script with shared constants, properties, and functions. Some demos extend it and use the base functions or build upon them.
# Example from PlayerPushRigidBody3D.gd
extends BasePlayerPlatformer3D
func _physics_process(delta: float) -> void:
apply_base_movement(delta)
push(delta)We’ll start by covering the base class and how we coded running and jumping before tackling the other ways to use .
Note that we wrote small functions to help you read the code and make it reusable.
We don’t necessarily recommend splitting your code this way in your games; we chose this style for the needs of the node essentials demos and this guide.
The node requires coding all of your movement logic by yourself. Here, you’ll get a detailed look at a base class for 3D platformer movement.
This code is more fleshed out as we took the time to name all the values we use with exported variables and create functions to make the code readable and reusable in other demos.
In this example, the character can:
The scene relies on one node and a . The AstronautSkin is the animated 3D mesh for the character.
To implement all the mechanics above, we need to tell Godot some values to know how to move our body. We store them as exported variables to make them editable in the Inspector.
export var move_speed := 6.0
export var acceleration := 6.0
export var jump_initial_impulse := 12.0
export var jump_additional_force := 4.5
export var rotation_speed := 12.0
export var snap_length := 0.5
export var do_stop_on_slope := true
export var has_infinite_inertia := trueWe then define a function to calculate the character’s motion and move it, which has four steps:
velocity.velocity if necessary.move_and_slide_with_snap() function.Here is the base input and orientating it to be relative to the .
_move_direction = _get_camera_oriented_input()
func _get_camera_oriented_input() -> Vector3:
var input_left_right := (
Input.get_action_strength("move_right")
- Input.get_action_strength("move_left")
)
var input_forward_back := (
Input.get_action_strength("move_down")
- Input.get_action_strength("move_up")
)
var raw_input := Vector2(input_left_right, input_forward_back)
var input := Vector3.ZERO
# This is to ensure that diagonal input isn't stronger than axis aligned input
input.x = raw_input.x * sqrt(1.0 - raw_input.y * raw_input.y / 2.0)
input.z = raw_input.y * sqrt(1.0 - raw_input.x * raw_input.x / 2.0)
input = _camera_controller.global_transform.basis.xform(input)
return inputThen we calculate the new velocity using the player input.
# We separate out the y velocity to not interpolate on the gravity
var y_velocity := _velocity.y
_velocity.y = 0.0
_velocity = _velocity.linear_interpolate(_move_direction * move_speed, acceleration * delta)
_velocity.y = y_velocityNext, we look at the character’s current state to modify the
velocity. It is important to set the snap value to
Vector2.ZERO whenever the player jumps. Otherwise, the
move_and_slide_with_snap() function keeps the astronaut
snapped to the surface in the direction of the snap vector.
if is_jumping():
_velocity.y = jump_initial_impulse
_snap = Vector3.ZERO
_model.jump()
elif is_air_boosting():
_velocity.y += jump_additional_force * delta
elif is_landing():
_snap = Vector3.DOWN * snap_length
_model.land()You can think of the snap vector like a ray cast from the ’s origin.
If it touches the floor, the move_and_slide_with_snap()
function snaps the character to the floor.
This function is handy when walking on a slope, preventing the character from hopping down each step.
As you can see, we use some functions we define ourselves to check if the character is jumping, landing, etc.
We wrote them that way to help you read the code and to reuse them across demos.
func is_jumping() -> bool:
return Input.is_action_just_pressed("jump_3d") and is_on_floor()
func is_air_boosting() -> bool:
return Input.is_action_pressed("jump_3d") and not is_on_floor() and _velocity.y > 0.0
func is_landing() -> bool:
return _snap == Vector3.ZERO and is_on_floor()Back to the apply_base_movement() function, we call
move_and_slide_with_snap() to move the character.
_velocity = move_and_slide_with_snap(
_velocity, _snap, Vector3.UP, do_stop_on_slope, 4, deg2rad(45), has_infinite_inertia
)We orient the character model to the direction the player is trying to move in.
# To not orient quickly to the last input, we save a last strong direction,
# this also ensures a good normalized value for the rotation basis.
if _move_direction.length() > 0.2:
_last_strong_direction = _move_direction.normalized()
_orient_character_to_direction(_last_strong_direction, delta)We rotate the model by calculating a new based on the input direction and using the
slerp() function to turn towards it smoothly. For more
information on working with rotations in 3D, see Using
3D Transforms in the Godot manual.
func _orient_character_to_direction(direction: Vector3, delta: float) -> void:
var left_axis := Vector3.UP.cross(direction)
var rotation_basis := Basis(left_axis, Vector3.UP, direction).orthonormalized()
_model.transform.basis = _model.transform.basis.orthonormalized().slerp(rotation_basis, delta * rotation_speed).scaled(_model.scale)We handle the rest of the visuals with a separate scene and script:
AstronautSkin.tscn and AstronautSkin.gd. We
forward the velocity every frame to adjust the animation
playback speed and forward jumping and landing events through function
calls.
_model.velocity = _velocity
if is_jumping():
_velocity.y = jump_initial_impulse
_snap = Vector3.ZERO
_model.jump()
elif is_air_boosting():
_velocity.y += jump_additional_force * delta
elif is_landing():
_snap = Vector3.DOWN * snap_length
_model.land()All the above is a base you can use to create platforming characters and we use it in the following examples.
In just a few lines of code, you can extend a base character controller with a stomping mechanic as in Mario games.
To stomp an enemy, we need to check if the player lands on the top of
them. If we use box collision shapes, we can directly use the
is_on_floor() function.
We loop through all the collisions that may have occurred after
calling move_and_slide() and ensure that we collided with
an enemy.
func stomp() -> void:
# If we fell on top of an enemy, KinematicBody considers that we are on
# the floor. We only run the stomp code if is_on_floor() returns true and
# we're landing this frame.
if not (is_landing() and is_on_floor()):
return
for index in get_slide_count():
# We loop over all the collisions this frame and if one of the things we
# collided with is an enemy, we destroy it and jump.
var collision := get_slide_collision(index)
# To detect enemies, we use a node group here, but you could also use
# the is keyword and check for a specific type.
#
# Or you could use duck typing and check that the entity has a "die"
# function for example.
if collision.collider.is_in_group("enemy"):
collision.collider.die()
_velocity.y += stomp_bump_strengthHere, we extend our BasePlayerPlatformer3D class and
call the parent class’s functions in _physics_process.
Afterward, we call the stomp function.
extends BasePlayerPlatformer3D
export var stomp_bump_strength := 4.0
func _physics_process(delta: float) -> void:
apply_base_movement(delta)
stomp()If you use collision shapes other than boxes,
is_on_floor() should work as long as the angle between your
character’s shape and the enemy’s shape is less than the maximum slope
angle at the collision point.
You can use to create moving platforms. You can
move them directly with move_and_slide(), like any moving
entity in your world. You can also use the animation player to design
moving platforms.
Each moving platform is a with an that animates its position. We use a looping animation for that.
In this case, you must use the Motion -> Sync to Physics option in the Inspector on the . It allows the physics engine to use changes in the node’s position when calculating collisions in the world.
Building upon moving platforms, we can use to move on rotating surfaces.
Like with moving platforms, you can use the animation player to animate their rotation.
To do so, you want to set the animation player to update on the physics callback and enable the platforms Sync To Physics property to interact with the character correctly.
In our example, we created a bridge that can raise and prevent the player from crossing a gap.
We can set the angle of slope players can climb with the sixth
parameter: floor_max_angle. This parameter takes a radian,
so we pass in a maximum of 45 degrees and convert it to
radians with the deg2rad() function.
To ensure the character actually slides on angled platforms, we need
to set the 4th stop_on_slope argument in
move_and_slide_snap() to false.
In our code, we use a property named do_stop_on_slope
that we pass to move_and_slide_with_snap(). All we have to
do is to toggle the property in the Inspector.
_velocity = move_and_slide_with_snap(
_velocity, _snap, Vector3.UP, do_stop_on_slope, 4, deg2rad(45), has_infinite_inertia
)Many games involve pushing world objects around to solve physics puzzles, as in the 3D Legend of Zelda and Tomb Raider games.
This example is about having a push mechanic in your game where the character can stop against a rock and push it.
Here, we focus on pushing a node: a rock controlled by the physics engine that keeps rolling after you push it.
Kinematic bodies can interact with other bodies. By default, if you
call move_and_slide() with the
infinite_inertia property set to true, your
kinematic body can move any rigid body out of its way.
What if you want to stop when touching a and control the pushing force instead?
To do so, you want to turn off infinite_inertia first.
Since we’ve exposed the has_infinite_inertia property, we
can set this in the Inspector on the player.
To push the body, we check if we are colliding with a rigid body, and if so, we apply an impulse to it in our direction of motion.
export var push_strength := 0.5
func push(delta: float) -> void:
# We have to always reset the property to only set it to true when the
# character is pushing something.
_is_pushing = false
for index in get_slide_count():
var collision := get_slide_collision(index)
var collider := collision.collider
# We loop over all the collisions that occured this frame and look for a
# RigidBody2D.
if collider is RigidBody:
# We ensure that one not above the body using a vector.product. We
# use it to compare the angle between the floor normal and the
# collision normal.
#
# A value lower than 0.9 means that the 2 vectors are at an angle
# lower than 90°.
_is_pushing = collision.normal.dot(Vector3.UP) < 0.9
_velocity = _move_direction * push_strength
# We apply an impulse to push the body we are colliding with.
collider.apply_central_impulse(_velocity.normalized() * push_strength * delta)
break
_model.is_pushing = _is_pushingWe call our push() function in
_physics_process().
func _physics_process(delta: float) -> void:
apply_base_movement(delta)
push(delta)As we apply an impulse to the rocks, they keep rolling for a while even when we stop pushing. You can control their behavior more accurately with the many properties.
We forward the _is_pushing property to the
AstronautSkin scene so it can set the animation.
_model.is_pushing = _is_pushingWall jumping is an excellent addition to a 3D platformer, letting players reach new locales. The ability is pervasive in the genre and is present in the 3D Mario games, Tomb Raider, Ratchet and Clank, Prince of Persia, and many more.
Here, we have a character that can stick to walls, slide down, fall off, and wall jump.
Note: Any property we don’t show below here comes
from the parent BasePlayerPlatformer3D class we showed
before: Basic 3D Platformer
movement.
We introduce a few new properties for the wall jump.
export var wall_friction_multiplier := 0.5
var _wall_normal: Vector3
var _input_direction: Vector3
var _wall_input: Vector3
var _is_locked_to_wall := false
onready var _wall_jump_timer: Timer = $WallJumpTimerIn the processing loop, we start by getting the camera-oriented input as usual.
_input_direction = _get_camera_oriented_input()Next, we check if the wall jump timer has stopped. If it has, we apply standard input to the character.
If the timer is running, then we are both on a wall and can wall
jump. We apply a mix of the wall’s normal and the player’s input, fading
it out with a lerp() function as the timer runs out. This
mix locks the player’s movement to the direction of the wall jump for a
time immediately after wall jumping.
func _physics_process(delta: float) -> void:
# ...
if _wall_jump_timer.is_stopped():
_move_direction = _input_direction
else:
_move_direction = lerp(
_input_direction, _wall_input, _wall_jump_timer.time_left / _wall_jump_timer.wait_time
)Before checking whether we are locked into wall sliding, we need to know the normal of any wall we are touching.
Here is the logic for setting the _is_locked_to_wall
variable and applying some friction to the character if they are wall
sliding, using several Boolean functions.
_wall_normal = get_wall_normal()
if _is_locked_to_wall:
_is_locked_to_wall = not is_pressing_away_from_wall()
else:
_is_locked_to_wall = is_on_wall() and is_pressing_against_wall()
if is_wall_sliding():
# We apply some friction to slow down the character's fall.
_velocity *= wall_friction_multiplier
# And we push the character towards the wall to make them stick to it.
_velocity += _wall_normal * _gravity * deltaWe check our jumping and landing Boolean functions with the addition
of is_wall_jumping().
if is_jumping():
_velocity.y = jump_initial_impulse
_snap = Vector3.ZERO
_model.jump()
elif is_wall_jumping():
_wall_jump_timer.start()
_velocity.y = jump_initial_impulse
_wall_input = _wall_normal
_snap = Vector3.ZERO
_model.jump()
elif is_air_boosting():
_velocity.y += jump_additional_force * delta
elif is_landing():
_snap = Vector3.DOWN * snap_length
_model.land()Finally, we call move_and_slide_with_snap() to move the
character.
func _physics_process(delta: float) -> void:
# ...
_velocity = move_and_slide_with_snap(
_velocity, _snap, Vector3.UP, do_stop_on_slope, 4, deg2rad(45), has_infinite_inertia
)
_model.velocity = _velocityHere are the functions that tell us if the character has just fallen, is wall jumping, or is sliding on a wall.
## Returns `true` if the character starts falling.
func is_just_falling() -> bool:
return not _anim_player.current_animation == "fall" and is_falling()
## Returns `true` if the character is initiating a wall-jump.
func is_wall_jumping() -> bool:
return is_on_wall() and Input.is_action_just_pressed("jump")
## Returns `true` if the character is sliding against a wall.
func is_wall_sliding() -> bool:
return is_on_wall() and not is_zero_approx(_horizontal_direction)To finish up, we’ll show a simple example of a first-person controller using .
Here we are using the common FPS controller used in several other
demos. Its located in common/PlayerFPS3D.
Using for an FPS is much the same as using it for a third person controller.
While the player script directly controls the for the FPS controller, the basics are the same.
func _physics_process(delta: float) -> void:
# ...
func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
_yaw_input = -event.relative.x * mouse_sensitivity
_tilt_input = -event.relative.y * mouse_sensitivityWe get the input and align it to the player. Then, we calculate the velocity.
var input := Vector3.ZERO
input.x = raw_input.x * sqrt(1.0 - raw_input.y * raw_input.y / 2.0)
input.z = raw_input.y * sqrt(1.0 - raw_input.x * raw_input.x / 2.0)
input = global_transform.basis.xform(input)
var y_velocity := _velocity.y
_velocity.y = 0.0
_velocity = _velocity.linear_interpolate(input * move_speed, delta * acceleration)
_velocity.y = y_velocityWe use bool functions to check for jumping and landing.
if is_jumping():
_velocity.y = jump_impulse
_snap = Vector3.ZERO
elif is_landing():
_snap = Vector3.DOWN * snap_lengthAnd we use move_and_slide_with_snap() to move the
controller.
_velocity = move_and_slide_with_snap(
_velocity, _snap, Vector3.UP, true, 4, deg2rad(45), true
)is excellent for any physical object you want to move manually with fine control. is the most commonly used node for characters in Godot.
Here is when you should use , , or instead:
Use a for solid walls or ground, even for objects that affect the player’s movement, like conveyor belts.
Use a for any physical object you want to move using physics properties like gravity and forces.
While you typically use them for physics games, some studios also use rigid body physics for all their game characters.
Unlike the above, does not handle collisions with the environment. It only detects what enters and leaves it, whether it’s another area, a physics body, or the mouse cursor.
You can use it whenever you need to have something happen when entering or touching an entity, but you don’t need collisions. You’ll find many examples of that in the Area guide.
Godot used to only have move_and_collide() until version
2.0. Later, the developers added the move_and_slide()
function to handle the most common ways people moved characters with a
.
Often, move_and_slide() is all you need as it gives you
several parameters to customize how the motion feels.
However, it doesn’t handle all the possible motions you would want for your games.
For one, you can’t use it to create mirroring lasers and other objects that bounce against walls. Although, if the shape of the projectile does not matter, you can use a instead.
For custom use cases move_and_collide() might be
needed.
In any case, we recommend trying to use move_and_slide()
or move_and_slide_with_snap() and only switch to
move_and_collide() when they show their limits. They are
both easy to replace as they often are just one function call in your
character script.
As with every other physics body or area, you want to use the physics layers and masks to control what your entity collides with.
Your body collides with every physics layer set in its mask property.
If you want to react to a collision with a specific body, there are a few strategies you can use to check which body you collided with specifically:
We detailed those strategies and their pros and cons in the guide: telling-bodies-apart.
Lastly, you can register exceptions, other bodies on a layer that you want to ignore.
For example, in a game with a non-playable character that accompanies the hero, you might want to ignore collisions with that NPC for a short time during certain scenes or interactions.
When that is the case, you can use the function to add and then remove an exception.
add_collision_exception_with(npc)
remove_collision_exception_with(npc)The class has a test_move
function that we rarely use, which can be very useful, especially when
polishing your game.
You can think of it like a , but that projects your whole character’s collision shapes instead of just a line.
One use case would be to prevent a character from ending a dash inside a wall: by testing the motion in advance, we know the character cannot get stuck regardless of the dash angle.
In Unity tutorials, you’ll sometimes see people use many ray casts to make platform characters move and detect features of the environment.
You can often use a single node and test_move()
instead in Godot.
If the test motion would cause a collision, the function returns
false. Otherwise, it returns true.